using System.Collections.Generic;

namespace Fadd.Data.Mappings
{
    /// <summary>
    /// Provides mappings between tables and classes.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Mappings are loaded using one or more different loaders. In this way
    /// you can provide your own special way of loading mappings, or use one of
    /// our built in loaders such as mirror loader and XmlLoader.
    /// </para>
    /// <para>
    /// You can also add mappings manually by using the <see cref="AddMapping"/> method.
    /// </para>
    /// </remarks>
    public class Provider
    {
        private readonly Dictionary<string, IMapping> _mappings = new Dictionary<string, IMapping>();
        private readonly List<ILoader> _loaders = new List<ILoader>();

        /// <summary>
        /// Add a mapping.
        /// </summary>
        /// <param name="mapping">Mapping file.</param>
        public void AddMapping(IMapping mapping)
        {
            Check.Require(mapping, "mapping");

            _mappings.Add(mapping.Type.Name, mapping);
        }

        /// <summary>
        /// Get a mapping file.
        /// </summary>
        /// <remarks>
        /// Will go through all different loaders until of of them return a mapping.
        /// All found mappings are cached.
        /// </remarks>
        /// <param name="className">class to fetch a mapping for.</param>
        /// <returns><see cref="IMapping"/> if found; otherwise null.</returns>
        public IMapping GetMapping(string className)
        {
            IMapping mapping;
            lock (_mappings)
                if (_mappings.TryGetValue(className, out mapping))
                    return mapping;

            foreach (ILoader loader in _loaders)
            {
                mapping = loader.GetMapping(className);
                if (mapping == null) continue;
                lock (_mappings)
                    if (!_mappings.ContainsKey(className))
                        _mappings.Add(className, mapping);
                return mapping;
            }

            return null;
        }

        /// <summary>
        /// Add a <see cref="ILoader"/>.
        /// </summary>
        /// <param name="loader"></param>
        public void Add(ILoader loader)
        {
            Check.Require(loader, "loader");

            _loaders.Add(loader);
        }
    }
}
